package xyz.canran.project.algorithm.lock;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Redis 分布式锁
 * @author  canran
 *
 */
@Service
public class RedisDistributedLockRedissonImpl {

    private static final Logger log = LoggerFactory.getLogger(RedisDistributedLockRedissonImpl.class);

    private final RedissonClient redissonClient;

    @Autowired
    public RedisDistributedLockRedissonImpl(RedissonClient redissonClient) {
        this.redissonClient =  redissonClient;
    }

    /**
     * 续期单分布式锁任务执行
     * @param name
     * @return
     */
    public boolean renewalSingleLockTask(String name) {
        String lockName = "redisson-test:" + name;
        // 1.获取一把锁，只要锁的名字一样，就是同一把锁
        RLock rlock = redissonClient.getLock(lockName);

        boolean lockResult = false;

        try {
            // 2.加锁 注意：tryLock无参方法才会调用续期，使用有参方法指定了最长时间不会续期
            lockResult = rlock.tryLock();
            if (lockResult) {
                // 执行逻辑
                TimeUnit.SECONDS.sleep(10L);
                log.info("lock success");
            } else {
                // 执行加锁失败逻辑
                log.info("lock fail");
            }

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            // 3. 解锁， 注意这儿需要判定 加锁成功 且 锁还在执行中才能解锁
            if (lockResult && rlock.isLocked()) {
                rlock.unlock();
                log.info("unlock success");
            }
        }
        log.info("lockName:{}", lockName);
        return lockResult;
    }


    /**
     * 固定时间单分布式锁任务执行
     * @param name
     * @return
     */
    public boolean fixedTimeSingleLockTask(String name) {

        // 加锁时间 3分钟
        long lockTime = 3 * 60 * 1000L;
        String lockName = "redisson-test:" + name;
        // 1.获取一把锁，只要锁的名字一样，就是同一把锁
        RLock rlock = redissonClient.getLock(lockName);

        boolean lockResult = false;

        try {
            // 2.加锁 注意：tryLock无参方法才会调用续期，使用有参方法指定了最长时间不会续期
            lockResult = rlock.tryLock(lockTime, TimeUnit.MILLISECONDS);
            if (lockResult) {
                // 执行逻辑
                TimeUnit.SECONDS.sleep(10L);
                log.info("lock success");
            } else {
                // 执行加锁失败逻辑
                log.info("lock fail");
            }

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            // 3. 解锁， 注意这儿需要判定 加锁成功 且 锁还在执行中才能解锁
            if (lockResult && rlock.isLocked()) {
                rlock.unlock();
                log.info("unlock success");
            }
        }
        log.info("lockName:{}", lockName);
        return lockResult;
    }

    /**
     * 自动续期多分布式锁任务执行
     * @param nameList
     * @return
     */
    public boolean renewalMultiLockTask(List<String> nameList) {

        List<RLock> lockList = nameList.stream().map(name -> {
            String lockName = "redisson-test:" + name;
            return redissonClient.getLock(lockName);
        }).toList();

        RLock[] lockArray = lockList.toArray(new RLock[0]);
        RLock multiLock = redissonClient.getMultiLock(lockArray);

        boolean lockResult = false;
        try {
            lockResult = multiLock.tryLock();
            if (lockResult) {
                log.info("lock success");
                TimeUnit.SECONDS.sleep(2L);

            } else {
                log.info("lock fail");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (lockResult) {
                multiLock.unlock();
            }
        }
        return lockResult;
    }


    /**
     * 固定时长多分布式锁任务执行
     * @param nameList
     * @return
     */
    public boolean fixedTimeMultiLockTask(List<String> nameList) {

        long lockTime = 3 * 60 * 1000L;

        List<RLock> lockList = nameList.stream().map(name -> {
            String lockName = "redisson-test:" + name;
            return redissonClient.getLock(lockName);
        }).toList();

        RLock[] lockArray = lockList.toArray(new RLock[0]);
        RLock multiLock = redissonClient.getMultiLock(lockArray);


        boolean lockResult = false;
        try {
            lockResult = multiLock.tryLock(lockTime, TimeUnit.MILLISECONDS);
            if (lockResult) {
                log.info("lock success");
                TimeUnit.SECONDS.sleep(2L);

            } else {
                log.info("lock fail");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (lockResult) {
                multiLock.unlock();
            }
        }
        return lockResult;
    }
}
